﻿// Deep Earth is a community project available under the Microsoft Public License (Ms-PL)
// Code is provided as is and with no warranty – Use at your own risk
// View the project and the latest code at http://DeepEarth.codeplex.com/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using GeoAPI.Geometries;

namespace DeepEarth.Client.MapControl.Geometries
{
    [TemplatePart(Name = PART_path, Type = typeof(Path))]
    public abstract class PathControlBase : GeometryControlBase<IGeometry>
    {
        private const string PART_path = "PART_path";
        protected Rect _Extent;
        protected Path _Path;
        protected Brush _Stroke;
        protected double _StrokeThickness;
        protected ObservableCollection<Location> _Locations;
        protected List<Point> _LogicalCoordinates;

        protected PathControlBase(IGeometry geometry)
            : base(geometry)
        {
            DefaultStyleKey = typeof(PathControlBase);
            this.GetLoaded().Subscribe(@event => OnApplyTemplate());
            // Loaded += (o, e) => OnApplyTemplate();
        }

        /// <summary>
        /// The collection of System.Windows.Point objects that make up the paths x, y coordinates
        /// </summary>
        public ObservableCollection<Location> Locations
        {
            get
            {
                if (_Locations == null)
                {
                    Locations = new ObservableCollection<Location>();
                }
                return _Locations;
            }
            set
            {
                _Locations = value;
                _Locations.CollectionChanged += _Locations_CollectionChanged;
                UpdatePointDependencies();
            }
        }

        /// <summary>
        /// Update the logical and geographic coordinates of the points comprising the PathBase
        /// </summary>
        private void UpdatePointDependencies()
        {
            UpdateLogicalCoords();
            UpdateExtent();
            UpdateGeometry();

            if (Layer != null) Layer.UpdateShape(this);
        }

        /// <summary>
        /// The logical coordinates comprising the PathBase
        /// </summary>
        public List<Point> LogicalCoordinates
        {
            get { return _LogicalCoordinates; }
        }

        /// <summary>
        /// Update the logical coordinate values
        /// </summary>
        private void UpdateLogicalCoords()
        {
            _LogicalCoordinates = new List<Point>();
            foreach (var location in Locations)
            {
                //  var location = new Location {Longitude = geoPoint.Longitude, Latitude = geoPoint.Latitude};
                _LogicalCoordinates.Add(Map.DefaultInstance.CoordHelper.GeoToLogical(location));
                // _LogicalCoordinates.Add(Map.DefaultInstance.CoordHelper.GeoToLogical(new Point(geoPoint.Longitude, geoPoint.Latitude)));
            }
        }

        internal abstract void UpdateGeometry();

        /// <summary>
        /// Update the rectangular bounds of the object
        /// </summary>
        private void UpdateExtent()
        {
            //Calculate the new extent
            double minx = double.MaxValue;
            double miny = double.MaxValue;
            double maxx = double.MinValue;
            double maxy = double.MinValue;

            foreach (var location in Locations)
            {
                if (location.Longitude < minx) minx = location.Longitude;
                if (location.Latitude < miny) miny = location.Latitude;
                if (location.Longitude > maxx) maxx = location.Longitude;
                if (location.Latitude > maxy) maxy = location.Latitude;
            }
            _Extent = new Rect(new Point(minx, miny), new Point(maxx, maxy));
        }

        void _Locations_CollectionChanged(object o, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                case NotifyCollectionChangedAction.Remove:
                case NotifyCollectionChangedAction.Replace:
                case NotifyCollectionChangedAction.Reset:
                    {
                        UpdatePointDependencies();
                        break;
                    }
            }
        }

        /// <summary>
        /// The system colour of the line portion of the PathBase
        /// </summary>
        public Brush Stroke
        {
            get { return _Stroke; }
            set
            {
                _Stroke = value;
                if (IsLoaded)
                {
                    _Path.Stroke = Stroke;
                    if (Layer != null) Layer.UpdateShape(this);
                }

            }
        }

        /// <summary>
        /// The thickness of the line portion of the PathBase
        /// </summary>
        public double StrokeThickness
        {
            get { return _StrokeThickness; }
            set
            {
                _StrokeThickness = value;
                if (IsLoaded)
                {
                    _Path.StrokeThickness = StrokeThickness;
                    if (Layer != null) Layer.UpdateShape(this);
                }
            }
        }

        /// <summary>
        /// The adjustment scale for the entire PathBase
        /// </summary>
        public override double ScaleAdjustment
        {
            get { return base.ScaleAdjustment; }
            set
            {
                if (value > double.MinValue && value < double.MaxValue)
                {
                    base.ScaleAdjustment = value;
                    if (_Path != null && _StrokeThickness != 0)
                    {
                        _Path.StrokeThickness = _StrokeThickness * base.ScaleAdjustment;
                    }
                }
            }
        }

        /// <summary>
        /// Handles the event raised by applying the FrameworkElement template
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _Path = (Path)GetTemplateChild(PART_path);
            _Path.StrokeThickness = StrokeThickness;
            _Path.Stroke = Stroke;

            IsLoaded = true;

            if (Layer != null) Layer.UpdateChildLocation(this);
        }

        /// <summary>
        /// The System.Windows.Media.PathGeometry of the PathData object
        /// </summary>
        public PathGeometry PathData
        {
            get { return (PathGeometry)_Path.Data; }
            set
            {
                if (_Path != null) _Path.Data = value;
            }
        }

        /// <summary>
        /// Specifies whether or not the geometry is continuous
        /// </summary>
        public abstract bool IsClosed { get; }


        /// <summary>
        /// Determines if the PathBase objects shares an overlap with a specified extent
        /// </summary>
        /// <param name="bounds"></param>
        /// <returns></returns>
        public override bool Intersects(Rect bounds)
        {
            var intersect = new Rect(bounds.X, bounds.Y, bounds.Width, bounds.Height);
            intersect.Intersect(_Extent);

            return (intersect.Width > 0 || intersect.Height > 0);
        }
    }
}